package com.qf.shop.order.service.impl;

import com.baomidou.dynamic.datasource.toolkit.DynamicDataSourceContextHolder;
import com.netflix.discovery.converters.Auto;
import com.qf.shop.common.constants.OrderConstatns;
import com.qf.shop.common.exception.BusinessException;
import com.qf.shop.common.pool.ShopExcutors;
import com.qf.shop.common.vo.*;
import com.qf.shop.order.entity.OrderDetailEntity;
import com.qf.shop.order.entity.OrderEntity;
import com.qf.shop.order.feign.api.AddressService;
import com.qf.shop.order.feign.api.CarService;
import com.qf.shop.order.feign.api.StockService;
import com.qf.shop.order.interceptor.LoginInterceptor;
import com.qf.shop.order.interceptor.OrderDynamicDatasourceInterceptor;
import com.qf.shop.order.mapper.OrderMapper;
import com.qf.shop.order.service.OrderService;
import com.qf.shop.order.utils.OrderUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {

    @Autowired
    private LoginInterceptor loginInterceptor;

    @Autowired
    private OrderUtils orderUtils;

    @Autowired
    private AddressService addressService;

    @Autowired
    private CarService carService;

    @Autowired
    private OrderMapper orderMapper;

    @Autowired
    private OrderDynamicDatasourceInterceptor orderDynamicDatasourceInterceptor;

    @Autowired
    private StockService stockService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    /**
     * 必须要考虑到分布式事务(订单被取消了或者下单没有超过，需要释放库存)
     *
     * @param orderVo
     * @return
     */
    @Override
    @Transactional
    public String create(OrderVo orderVo) throws ExecutionException, InterruptedException {

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();

//        // 1.启动一个任务查询用户的地址
        CompletableFuture<AddressVo> addressFuture = CompletableFuture.supplyAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            return addressService.getAddressVoById(orderVo.getAid());
        }, ShopExcutors.getExecutor());

        // 2.启动一个任务查询用户的购车
        CompletableFuture<UserCarVo> carFuture = CompletableFuture.supplyAsync(() -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            UserCarVo userCar = carService.getUserCar(orderVo.getSelectGid());
            log.debug("userCar:{}", userCar);
            return userCar;
        }, ShopExcutors.getExecutor());

        carFuture.get();

        // 3.启动一个任务扣减库存
        CompletableFuture<Boolean> stockFuture = carFuture.thenApplyAsync((userCarVo) -> {
            RequestContextHolder.setRequestAttributes(requestAttributes);
            List<StockVo> stockVoList = new ArrayList();
            for (StoreCarVo storeCarVo : userCarVo.getStoreCarVoList()) {
                for (CarVo carVo : storeCarVo.getCarVoList()) {
                    StockVo stockVo = new StockVo();
                    stockVo.setGid(carVo.getGid());
                    stockVo.setCount(carVo.getCount());
                    stockVoList.add(stockVo);
                }
            }
            return stockService.lockStock(stockVoList);
        }, ShopExcutors.getExecutor());

//        CompletableFuture<Void> future1 = CompletableFuture.allOf(carFuture, stockFuture);
        Boolean aBoolean = stockFuture.get();
        if (!aBoolean) {
            throw new BusinessException("扣减库存失败", 3001);
        }

        // 4、创建一个任务阻塞
        CompletableFuture<Void> future = CompletableFuture.allOf(addressFuture, stockFuture);
        future.get();// 地址任务和库存任务全部执行完

        // 5.先获取登录用户
        UserVo userVo = loginInterceptor.get();

        // 6.创建订单ID(yyyMMdd+用户ID后四位+4位随机数+流水号(唯一))
        String orderId = orderUtils.createOrderId(userVo.getId());

        // 7、获取表的索引
        Integer tabIndex = orderDynamicDatasourceInterceptor.get();

        AddressVo addressVo = addressFuture.get();
        UserCarVo userCarVo = carFuture.get();

        // 8.封装订单数据，插入订单数据
        CompletableFuture<Void> orderFuture = CompletableFuture.runAsync(() -> {

            orderDynamicDatasourceInterceptor.setOrderDataSource(userVo); // 重新设置

            OrderEntity orderEntity = new OrderEntity();
            orderEntity.setId(orderId);
            orderEntity.setUid(Integer.parseInt(userVo.getId()));
            orderEntity.setAddress(addressVo.getAddress());
            orderEntity.setPhone(addressVo.getPhone());
            orderEntity.setName(addressVo.getName());
            orderEntity.setStatus(1); // 1
            orderEntity.setTotalPrice(userCarVo.getTotalPrice());
            orderEntity.setCreateTime(new Date());
            orderEntity.setPayType(orderVo.getPayType());

            log.debug("orderEntity:{}", orderEntity);
            int order = orderMapper.createOrder(orderEntity, tabIndex);
            log.debug("order:{}", order);
        }, ShopExcutors.getExecutor());

        // 9、插入订单详情
        CompletableFuture<Void> orderDetailfuture = CompletableFuture.runAsync(() -> {
            orderDynamicDatasourceInterceptor.setOrderDataSource(userVo); // 重新设置
            List<OrderDetailEntity> odList = new ArrayList<>();
            for (StoreCarVo storeCarVo : userCarVo.getStoreCarVoList()) {
                for (CarVo carVo : storeCarVo.getCarVoList()) {
                    // 把用户购买的数据封装到订单闲情中
                    OrderDetailEntity odEntity = new OrderDetailEntity();
                    odEntity.setCount(carVo.getCount());
                    odEntity.setGdesc(carVo.getGoodsVo().getGdesc());
                    odEntity.setGid(carVo.getGid());
                    odEntity.setGprice(carVo.getGoodsVo().getGprice());
                    odEntity.setGname(carVo.getGoodsVo().getGname());
                    odEntity.setGurl(carVo.getGoodsVo().getGoodsPngList().get(0).getUrl());
                    odEntity.setOid(orderId);
                    odEntity.setSid(storeCarVo.getStoreVo().getId()); //
                    odEntity.setSubTotal(carVo.getSubTotal());
                    odList.add(odEntity);
                }
            }
            // 批量插入订单详情
            int odCount = orderMapper.batchInsertOrderDetail(odList, tabIndex);
            log.debug("odCount:{}", odCount);
        }, ShopExcutors.getExecutor());

        // 订单任务和订单详情任务全部执行完后再返回订单ID
        CompletableFuture<Void> odFulure = CompletableFuture.allOf(orderFuture, orderDetailfuture);
        odFulure.get(); // 阻塞

        // 清空购物车
        carService.clearUserCar(userVo.getId(), orderVo.getSelectGid());

        // 开始倒计时(做订单超时了)
        rabbitTemplate.convertAndSend(OrderConstatns.ORDER_EXCHANGE, "order.create", orderId);


        return orderId;
    }

    @Override
    public OrderEntity getOrderById(String orderId) {

        // 1.获取订单表的编号,在数据库的编号在拦截器中已经设置了
        Integer tabIndex = orderDynamicDatasourceInterceptor.get();
        if (tabIndex == null) {
            tabIndex = setOrderDataSource(orderId);
        }

        // 2.查询数据库
        return orderMapper.getOrderById(orderId, tabIndex);
    }

    // 因为修改订单的是支付宝回调，所以不会在拦截器中设置数据源，也没有当前登录的用户
    @Override
    public void updateOrderStatus(String orderId, Integer status) {
        Integer tabIndex = setOrderDataSource(orderId);

        // 5.更新订单状态
        orderMapper.updateOrderStatus(orderId, tabIndex, status);
    }

    @Override
    public List<StockVo> getStockVoListOrderId(String orderId) {

        // 1.设置数据源
        Integer tabIndex = setOrderDataSource(orderId);

        // 2.查询订单详情
        return orderMapper.getStockVoListOrderId(orderId,tabIndex);
    }

    public Integer setOrderDataSource(String orderId) {

        // 1.从订单ID中截取用户ID后四位
        Integer lastUserId = orderUtils.getLastUserIdByOrderId(orderId);

        // 2.计算数据库编号
        Integer dbIndex = (lastUserId % OrderConstatns.ORDER_DB_SIZE) + 1;

        // 3.计算表的索引
        Integer tabIndex = (lastUserId / OrderConstatns.ORDER_DB_SIZE % OrderConstatns.ORDER_TABLE_SIZE) + 1;

        // 4.设置数据源
        DynamicDataSourceContextHolder.push(OrderConstatns.ORDER_DATASOURCE_PRE + dbIndex);
        return tabIndex;
    }
}
